package com.file.parser;

import com.file.bo.ResponseData;
import com.file.bo.mail.LzBank;
import com.file.bo.mail.LzBankTran;
import com.file.constant.ErrorCode;
import com.file.util.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import technology.tabula.*;
import technology.tabula.detectors.SpreadsheetDetectionAlgorithm;
import technology.tabula.extractors.SpreadsheetExtractionAlgorithm;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
public class LzBankPdfParser extends BasePdfParser {

    public ResponseData<String> parseLzBankPdfToJson(String daId, String filePath) {
        log.info("parseLzBankPdfToJson started, daId:{}", daId);
        String json = null;

        try {
            LzBank lzBank = parseLzBankPdf(filePath);
            json = JsonUtils.convertObjectToJson(lzBank);
        } catch (Exception e) {
            log.error("OnError|{}||{}|{}|{}|{}", ErrorCode.FILE_PARSE_EXCEPTION.getCode(), daId, "", "", "parseLzBankPdfToJson failed", e);
            return new ResponseData<String>(json, ErrorCode.FILE_PARSE_EXCEPTION.getCode(),
                    ErrorCode.FILE_PARSE_EXCEPTION.getMsg());
        }

        log.info("parseLzBankPdfToJson completed, daId:{}", daId);
        return new ResponseData<String>(json, ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg());
    }

    private LzBank parseLzBankPdf(String filePath) {
        LzBank pab = parseLzBankHeader(filePath);
        List<LzBankTran> lzBankTrans = parseLzBankTrans(filePath);

        pab.setLzBankTrans(lzBankTrans);
        return pab;
    }

    private List<LzBankTran> parseLzBankTrans(String filePath) {
        List<LzBankTran> lzBankTrans = new ArrayList<>();

        List<List<String>> tranFieldsList = parseFileToRowList(filePath);
        for (int i = 0; i < tranFieldsList.size(); i++) {
            List<String> row = tranFieldsList.get(i);
            if (!row.get(0).matches("\\d+")) {
                continue;
            }
            if (row.size() == 10) {
                LzBankTran lzBankTran = new LzBankTran();
                lzBankTran.setId(row.get(0).trim());
                lzBankTran.setTransactionDate(row.get(1).trim());
                lzBankTran.setIncomeExpenseAmount(row.get(2).trim());
                lzBankTran.setBalance(row.get(3).trim());
                lzBankTran.setTransactionInstitution(row.get(4).trim());
                lzBankTran.setCounterpartyName(row.get(5).trim());
                lzBankTran.setCounterpartyAccount(row.get(6).trim());
                lzBankTran.setCashTransferFlag(row.get(7).trim());
                lzBankTran.setTransactionChannel(row.get(8).trim());
                lzBankTran.setSummary(row.get(9).trim());
                lzBankTrans.add(lzBankTran);
            }
        }
        return lzBankTrans;
    }

    private List<List<String>> parseFileToRowList(String filePath) {
        // 1. 读取文件
        File pdf = new File(filePath);

        //页面所有table的记录，每一个元素代表下面一条记录cellList
        List<List<String>> rowList = new ArrayList<>();

        // 2. pdfbox读取PDDocument
        try (PDDocument pdfDocument = PDDocument.load(pdf)) {
            // 3. tabula新建ObjectExtractor和NurminenDetectionAlgorithm，同时准备接收表格Rectangle的结构
            ObjectExtractor objectExtractor = new ObjectExtractor(pdfDocument);
            SpreadsheetDetectionAlgorithm detectionAlgorithm = new SpreadsheetDetectionAlgorithm();
            Map<Integer, List<Rectangle>> pageNumber2RectangleListMap = new HashMap<>();//key为页的序号， value为对应的Rectangle的list

            // 4. 获取每页的PageIterator
            PageIterator pageIterator = objectExtractor.extract();

            // 5. 解析每页的Rectangle
            // 如果单页pdf中有多个Rectangle，则解析出来的List<Rectangle>会有多个对象
            while (pageIterator.hasNext()) {
                Page page = pageIterator.next();
                List<Rectangle> rectangleList = detectionAlgorithm.detect(page);
                if (rectangleList.size() > 0) {
                    pageNumber2RectangleListMap.put(page.getPageNumber(), rectangleList);
                }
            }

            // 6.通过table位置获取表格具体内容
            SpreadsheetExtractionAlgorithm extractionAlgorithm = new SpreadsheetExtractionAlgorithm();

            // 如果单页pdf中有多个表格，则解析出来的List<Rectangle>会有多个对象
            for (Map.Entry<Integer, List<Rectangle>> entry : pageNumber2RectangleListMap.entrySet()) {
                Page page = objectExtractor.extract(entry.getKey());
                Rectangle rectangle = null;

                List<Rectangle> rectangleList = entry.getValue();
                for (Rectangle r : rectangleList) {
                    double rectangleHeight = r.getHeight();
                    if (Double.compare(rectangleHeight, 0) > 0 && Double.compare(r.getWidth(), 0) > 0) {
                        rectangle = r;
                        break;
                    }
                }
                if (rectangle == null) {
                    throw new RuntimeException("rectangle is null.");
                }

                if (entry.getKey() > 1) {
                    rectangle.setTop(rectangle.getTop());
                }
                rectangle.setBottom(rectangle.getBottom() + 50);
                Page area = page.getArea(rectangle);

                // 如果每页有多个表格，解析每一个table
                List<Table> tableList = extractionAlgorithm.extract(area);
                for (Table table : tableList) {
                    for (int i = 0; i < table.getRowCount(); i++) {
                        List<String> cellList = new ArrayList<>();//一条记录，每个单元格占一个元素
                        for (int j = 0; j < table.getColCount(); j++) {
                            cellList.add(table.getCell(i, j).getText(false));
                        }
                        rowList.add(cellList);
                    }
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }

        return rowList;
    }

    private LzBank parseLzBankHeader(String filePath) {
        LzBank lzBank = new LzBank();
        String pdfHeaderText = parsePdfHeaderText(filePath);
        // 提取各字段数据
        String startDate = extractValue(pdfHeaderText, "起止日期：", "用户姓名：");
        String userName = extractValue(pdfHeaderText, "用户姓名：", "银行账户：");
        String bankAccount = extractValue(pdfHeaderText, "银行账户：", "总记录数：");
        String totalRecordsStr = extractValue(pdfHeaderText, "总记录数：", "打印时间：");
        String printTime = extractValue(pdfHeaderText, "打印时间：", "序号");

        lzBank.setStartDate(startDate);
        lzBank.setUserName(userName);
        lzBank.setBankAccount(bankAccount);
        lzBank.setTotalRecords(totalRecordsStr);
        lzBank.setPrintTime(printTime);

        return lzBank;
    }

    private String extractValue(String text, String startKey, String endKey) {
        if (text == null || !text.contains(startKey)) return "";

        int startIndex = text.indexOf(startKey) + startKey.length();
        int endIndex = (endKey != null && text.contains(endKey)) ? text.indexOf(endKey) : text.length();

        return text.substring(startIndex, endIndex).trim();
    }


    public String getPdfTextByStripper(String filePath) {
        String pdfText = "";
        try (PDDocument pdDocument = PDDocument.load(new File(filePath))) {
            PDFTextStripper stripper = new PDFTextStripper();
            stripper.setSortByPosition(false);
            stripper.setStartPage(1);
            stripper.setEndPage(pdDocument.getNumberOfPages());
            pdfText = stripper.getText(pdDocument);
        } catch (Exception e) {
            log.error("OnError|{}||{}|{}|{}|{}", ErrorCode.FILE_PARSE_EXCEPTION.getCode(), "", "", "", "getPdfTextByStripper failed", e);
            throw new RuntimeException(e);
        }
        return pdfText;
    }

    @Override
    public String parsePdfHeaderText(String filePath) {
        String pdfHeaderText = "";
        try (PDDocument pdDocument = PDDocument.load(new File(filePath))) {
            PDFTextStripper stripper = new PDFTextStripper();
            stripper.setSortByPosition(true);
            stripper.setStartPage(1);
            stripper.setEndPage(1);
            pdfHeaderText = stripper.getText(pdDocument);
            pdfHeaderText = pdfHeaderText.replace(System.getProperty("line.separator", "\n"), "");
        } catch (Exception e) {
            log.error("OnError|{}||{}|{}|{}|{}", ErrorCode.FILE_PARSE_EXCEPTION.getCode(), "", "", "", "parsePdfHeaderText failed", e);
            throw new RuntimeException(e);
        }
        return pdfHeaderText;
    }

    public static void main(String[] args) {
        LzBankPdfParser lzBankPdfParser = new LzBankPdfParser();
        String json = lzBankPdfParser.parseLzBankPdfToJson("","D:\\data\\file\\lzbank\\兰州银行-1页.pdf").getData();
        System.out.println(json);

        json = lzBankPdfParser.parseLzBankPdfToJson("","D:\\data\\file\\lzbank\\兰州银行-多页.pdf").getData();
        System.out.println(json);

    }

}
